package com.dingding.transfer.stamper.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.http.HttpException;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.dingding.transfer.stamper.constant.ApprovalPara;
import com.dingding.transfer.stamper.constant.Code;
import com.dingding.transfer.stamper.constant.StamperPara;
import com.dingding.transfer.stamper.constant.StamperUrl;
import com.dingding.transfer.stamper.dao.*;
import com.dingding.transfer.stamper.dto.*;
import com.dingding.transfer.stamper.entity.*;
import com.dingding.transfer.stamper.service.BusinessService;
import com.dingding.transfer.stamper.service.CallBackService;
import com.dingding.transfer.stamper.service.DingDingService;
import com.dingding.transfer.stamper.util.GsonUtil;
import com.google.gson.reflect.TypeToken;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.aop.framework.AopContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author wangxiyuan
 * @description
 * @createDate 2021/3/31 9:29
 **/
@Service
@Slf4j
@Transactional
public class BusinessServiceImpl implements BusinessService {
    @Autowired
    private ProcessRecordDao   processRecordDao;

    @Autowired
    private ProcessDetailDao processDetailDao;

    @Autowired
    private FormDetailDao formDetailDao;

    @Autowired
    private UserDao userDao;

    @Autowired
    private PushRecordDao pushRecordDao;

    @Autowired
    private DeviceMapperDao deviceMapperDao;

    @Autowired
    private CompInfoDao compInfoDao;

    @Autowired
    private StamperPara stamperPara;

    @Autowired
    private CallBackService callBackService;

    @Autowired
    private DingDingService dingDingService;

    /**
     * 当设备不在线时，推送失败，开启定时任务每隔1min 推送申请单，当指纹没有录入的时候，先推送指纹录入命令
     * 并不知道此时使用者是谁，
     */
//    @Scheduled(initialDelay=5000,fixedDelay = 60000)
    public void rePush(){
        log.info("申请单失败重推");
        List<PushRecord> pushRecords = pushRecordDao.selectList(new QueryWrapper<PushRecord>().lambda().eq(PushRecord::getStatus, false));
        if(CollectionUtil.isNotEmpty(pushRecords)){
            pushRecords.stream().forEach(pushRecord -> {
                ApplyInfo applyInfo = findApplyInfo(pushRecord.getProcessId());
                List<FingerDetail> fingerDetails = getFingerList(pushRecord.getUuid(), true);
                if(CollectionUtil.isNotEmpty(fingerDetails)){
                    for (FingerDetail fingerDetail :
                            fingerDetails) {
                        if (fingerDetail.getFingerUserId().equals(applyInfo.getUserid())) {
                            pushRecord(pushRecord);
                            return;
                        }
                    }
                }
                // 没有录入指纹
                FingerAddReq fingerAddReq = new FingerAddReq();
                fingerAddReq.setUserId(applyInfo.getUserid());
                fingerAddReq.setUuid(applyInfo.getUuid());
                fingerAddReq.setUserName(applyInfo.getUserName());
                fingerAdd(fingerAddReq);
            });
        }
    }

    @Override
    public BaseResult saveProcessDetail(ProcessDetail processDetail) {
        try {
            processDetailDao.insert(processDetail);
        } catch (Exception e) {
            log.error("申请记录为{},保存失败{}",processDetail.toString(),e.getMessage());
            return BaseResultFactory.produceEmptyResult(Code.ERROR,e.getMessage());
        }
        return BaseResultFactory.produceSuccess();
    }

    @Override
    public BaseResult updateProcessStatus(String processId,int status) {
        ProcessDetail processDetail;
        try {
            QueryWrapper<ProcessDetail> wrapper = new QueryWrapper<>();
            wrapper.eq("process_id",processId);
            processDetail = processDetailDao.selectOne(wrapper);
            processDetail.setStatus(status);
            processDetailDao.updateById(processDetail);
        } catch (Exception e) {
            log.error("申请实例为{},更新状态失败{}",processId,e.getMessage());
            return BaseResultFactory.produceEmptyResult(Code.ERROR,e.getMessage());
        }
        return BaseResultFactory.produceSuccess(processDetail.getApplyId());
    }

    /**
     *
     * @param applyId 这里的申请Id并非钉钉 审批记录的Id,而是我们自己的申请Id，即ProcessDetail主键
     * @return
     */
    @Override
    public ApplyInfo findApplyInfo(String applyId) {
        // 获取申请记录
        ProcessDetail processDetail = processDetailDao.selectById(applyId);
        if(processDetail != null){
            ApplyInfo applyInfo = new ApplyInfo();
            applyInfo.setApplyId(applyId);
            // 获取对应的用户
            User user = userDao.selectOne(new QueryWrapper<User>().eq("userid", processDetail.getUserid()));
            applyInfo.setUserid(user.getSerialId().toString());
            applyInfo.setUserName(user.getName());
            List<FormDetail> details = formDetailDao.selectList(new QueryWrapper<FormDetail>().lambda().eq(FormDetail::getApplyId, processDetail.getApplyId()));
            if(CollectionUtil.isNotEmpty(details)){
                // 这里需要注意 如果声明表单中文与这个不符，会有问题
                for (FormDetail formDetail :
                        details) {
                    if (ApprovalPara.STAMPER_USING_TIME.equals(formDetail.getChineseName())){
                        applyInfo.setApplyNum(Integer.parseInt(formDetail.getValue()));
                    }
                    if (ApprovalPara.STAMPER_TYPE.equals(formDetail.getChineseName())
                            && StringUtils.isBlank(applyInfo.getUuid())){
                        // 这里类型映射 将对应印章的设备uuid填入
                        applyInfo.setUuid(((BusinessService)AopContext.currentProxy()).getDeviceId(formDetail.getValue()));
                    }
                }
            }
            return applyInfo;
        }else{
            log.error("获取不到对应编号为{}的申请实例！！！",applyId);
            return null;
        }
    }

    @Override
    public ProcessDetail findApplyDetail(String userid,String uuid) {
        log.info("根据用户Id{}获取设备{}最新的申请记录",userid,uuid);
        List<ProcessDetail> applyDetails = processDetailDao.getApplyDetail(userid,uuid);
        if(CollectionUtil.isNotEmpty(applyDetails)){
            return applyDetails.get(0);
        }
        return null;
    }

    @Override
    public BaseResult batchSaveProcessContent(String corpId,String processId) {
        try {
            ProcessDetail processDetail = processDetailDao.selectOne(new QueryWrapper<ProcessDetail>().eq("process_id",processId));
            if(processDetail == null){
                log.error("{}申请记录找不到",processId);
                return BaseResultFactory.produceEmptyResult(Code.APPLY_DETAIL_MISSING);
            }
            JSONObject instanceInfo = dingDingService.getInstanceInfo(corpId,processId);
            if(instanceInfo != null){
                JSONObject processInstance = JSONObject.parseObject(instanceInfo.getString("process_instance"));
                // 获取表单信息
                String formJson = processInstance.getString("form_component_values");
                List<FormComponent> formComponentList = GsonUtil.fromJson(formJson, new TypeToken<List<FormComponent>>() {
                }.getType());

                // 保存申请表单记录
                if(CollectionUtil.isNotEmpty(formComponentList)){
                    formComponentList.forEach(formComponent -> {
                        try {
                            FormDetail detail = new FormDetail();
                            detail.setApplyId(processDetail.getApplyId().toString());
                            detail.setChineseName(formComponent.getName());
                            if (ApprovalPara.STAMPER_TYPE.equals(formComponent.getName())){
                                processDetailDao.update(null,new UpdateWrapper<ProcessDetail>()
                                        .eq("apply_id",processDetail.getApplyId()).set("uuid", ((BusinessService)AopContext.currentProxy()).getDeviceId(formComponent.getValue())));
                            }
                            detail.setValue(StringUtils.equals(formComponent.getValue(),"null") ? "" : formComponent.getValue());
                            formDetailDao.insert(detail);
                        } catch (Exception e) {
                            log.error("实例ID为{}申请记录保存失败{}",processId,e.getMessage());
                        }
                    });
                }
                String opeartionJson = processInstance.getString("operation_records");
                List<OperationRecord> operationRecords = GsonUtil.fromJson(opeartionJson, new TypeToken<List<OperationRecord>>() {
                }.getType());
                if(CollectionUtil.isNotEmpty(operationRecords)){
                    operationRecords.forEach(operationRecord -> {
                        ProcessRecord processRecord = ProcessRecord.builder().userid(operationRecord.getUserid())
                                .result(operationRecord.getOperationResult())
                                .remark(operationRecord.getRemark())
                                .processTime(operationRecord.getDate())
                                .operationType(operationRecord.getOperationType())
                                .applyId(processDetail.getApplyId().toString())
                                .build();
                        try {
                            processRecordDao.insert(processRecord);
                        } catch (Exception e) {
                            log.error("实例ID为{}审批记录保存失败{}",processId,e.getMessage());
                        }
                    });
                }
            }
        } catch (Exception e) {
            log.error("实例ID为{}获取详情失败{}",processId,e.getMessage());
            return BaseResultFactory.produceEmptyResult(Code.ERROR,e.getMessage());
        }
        return BaseResultFactory.produceSuccess();
    }

    @Override
    public void updatePushRecord(PushRecord pushRecord) {
        pushRecord.update(new UpdateWrapper<PushRecord>().lambda().eq(PushRecord::getUuid, pushRecord.getUuid())
                .eq(PushRecord::getProcessId, pushRecord.getProcessId()));
    }

    @Override
    public BaseResult checkPushRecord(String uuid, String applyId, String userid,int applyNum) {
        // 找到对应的推送记录
        PushRecord pushRecord = pushRecordDao.selectOne(new QueryWrapper<PushRecord>().lambda().eq(PushRecord::getUuid, uuid)
                .eq(PushRecord::getProcessId, applyId).eq(PushRecord::getUserid, userid));
        if(pushRecord == null){
            pushRecord = new PushRecord().setStatus(true).setProcessId(applyId).setApplyCount(applyNum)
                    .setUserid(userid).setUuid(uuid).setSendTime(new Date()).setUseCount(0);
            pushRecord.insert();
        }else if(pushRecord.getUseCount() == pushRecord.getApplyCount()){
            return BaseResultFactory.produceEmptyResult(Code.HAVE_PUSHED);
        }else{
            // 更新 推送时间和状态
            pushRecord.setSendTime(new Date()).setStatus(true).update(new UpdateWrapper<PushRecord>().lambda().eq(PushRecord::getUuid, uuid)
                    .eq(PushRecord::getProcessId, applyId).eq(PushRecord::getUserid, userid));
        }
        // 将使用次数 放入返回结果中
        return BaseResultFactory.produceSuccess(pushRecord.getUseCount());
    }

    @Override
    public BaseResult pushRecord(ApplyInfo applyInfo) {
        String response = null;
        try {
            if(applyInfo == null){
                throw new RuntimeException(Code.APPLY_DETAIL_MISSING.getMsg());
            }
            Map<String, Object> reqData = new HashMap<>(5);
            reqData.put("uuid",applyInfo.getUuid());
            reqData.put("applicationId", applyInfo.getApplyId());
            reqData.put("userId", applyInfo.getUserid());
            reqData.put("totalCount", applyInfo.getApplyNum());
            // 针对相同的 uuid applicationId userId 已经推送并且使用次数超过申请次数了
            BaseResult result = checkPushRecord(applyInfo.getUuid(),applyInfo.getApplyId(),applyInfo.getUserid(),applyInfo.getApplyNum());
            if(!result.isSuccess()){
                throw new RuntimeException(result.getMessage());
            }
            reqData.put("needCount", result.getData());
            String stamperToken = callBackService.getStamperToken();
            //发送请求
            HttpResponse httpResponse = HttpRequest.post(StamperUrl.PATH_PUSH_APPLICATION)
                    .header("tToken", stamperToken)
                    .form(reqData)
                    .execute();
            if(httpResponse.isOk()){
                response = httpResponse.body();
                JSONObject jsonObject = JSONObject.parseObject(response);
                if(!StringUtils.equals("0",jsonObject.getString("code"))){
                    throw new RuntimeException(jsonObject.getString("message"));
                }
            }
        } catch (Exception e) {
            log.error("推送申请单出现错误{}",e.getMessage());
            updatePushRecord(new PushRecord().setStatus(false).setProcessId(applyInfo.getApplyId()).setUuid(applyInfo.getUuid()));
            return BaseResultFactory.produceEmptyResult(Code.ERROR,e.getMessage());
        }
        //解析结果
        return JSONObject.parseObject(response, BaseResult.class);
    }

    @Override
    public BaseResult pushRecord(PushRecord pushRecord) {
        ApplyInfo applyInfo = new ApplyInfo();
        applyInfo.setUuid(pushRecord.getUuid());
        applyInfo.setApplyId(pushRecord.getProcessId());
        applyInfo.setApplyNum(pushRecord.getApplyCount());
        applyInfo.setNeedNum(pushRecord.getUseCount());
        applyInfo.setUserid(pushRecord.getUserid());
        return pushRecord(applyInfo);
    }

    @Override
    public BaseResult pushRecord(String uuid, String applyId) {
        ApplyInfo applyInfo = findApplyInfo(applyId);
        applyInfo.setUuid(uuid);
        return pushRecord(applyInfo);
    }

    @Override
    public BaseResult fingerAdd(FingerAddReq data) {
        String s = null;
        try {
            //参数组装
            Map<String, Object> reqData = new HashMap<>(3);
            reqData.put("uuid", data.getUuid());
            reqData.put("userId", data.getUserId());
            reqData.put("username", data.getUserName());
            String stamperToken = callBackService.getStamperToken();
            HttpResponse httpResponse = HttpRequest.post(StamperUrl.PATH_FINGER_ADD)
                    .header("tToken", stamperToken)
                    .form(reqData)
                    .execute();
            if(httpResponse.isOk()){
                s = httpResponse.body();
            }
            log.debug("指纹录入：" + s);
            //解析结果
            return JSONObject.parseObject(s, BaseResult.class);
        } catch (HttpException e) {
            log.error("指纹录入出现错误{}",e.getMessage());
            return BaseResultFactory.produceEmptyResult(Code.ERROR,e.getMessage());
        }
    }

    @Override
    public List<FingerDetail> getFingerList(String uuid, boolean page) {
        Map<String, Object> reqData = new HashMap<>();
        reqData.put("uuid", uuid);
        reqData.put("page", page);
        reqData.put("pageNum", 0);
        reqData.put("pageSize", 100);
        String stamperToken = callBackService.getStamperToken();
        String body;
        //发送请求
        try {
            HttpResponse httpResponse = HttpRequest.post(StamperUrl.PATH_FINGER_LIST)
                    .header("tToken", stamperToken)
                    .form(reqData)
                    .execute();
            if(httpResponse.isOk()){
                body = httpResponse.body();
                log.debug("指纹列表：" + body);
                JSONObject jsonObject = JSONObject.parseObject(body);
                return JSONObject.parseObject(jsonObject.getJSONObject("data").getString("list"), new TypeToken<List<FingerDetail>>() {
                }.getType());
            }
        } catch (Exception e) {
            log.error("获取指纹列表失败{}",e.getMessage());
            return null;
        }
        return null;
    }

    /**
     * 查询用户
     * @param userName 传参可能是用户姓名 或者 钉钉用户userId
     * @return
     */
    @Cacheable(value = "USER_INFO",key="#p0")
    @Override
    public User findUserInfo(String userName) {
        // 同一个公司 出现同名的人的概率不大 这里就不做判断了
        User user = userDao.selectOne(new QueryWrapper<User>().eq("name",userName)
                .or().eq("userid",userName));
        return user;
    }

    @Override
    public List<StamperRecord> getStamperRecords(String applyId, String uuid) {
        String dingUrl = StamperUrl.PATH_RECORD_LIST;
        Map<String,Object> para = new HashMap<>();
        para.put("uuid",new String[]{uuid});
        para.put("applicationId",new String[]{applyId});
        para.put("page",true);
        para.put("pageNum",0);
        para.put("pageSize",100);
        String stamperToken = callBackService.getStamperToken();
        try {
            HttpResponse httpResponse = HttpRequest.post(dingUrl)
                    .header("tToken", stamperToken)
                    .form(para)
                    .execute();
            String body;
            if(httpResponse.isOk()){
                body = httpResponse.body();
                log.debug("加盖印章记录：" + body);
                JSONObject jsonObject = JSONObject.parseObject(body);
                return JSONObject.parseObject(jsonObject.getJSONObject("data").getString("list"), new TypeToken<List<StamperRecord>>() {
                }.getType());
            }
        } catch (HttpException e) {
            log.error("获取加盖印章记录失败{}",e.getMessage());
        }
        return null;
    }

    @Cacheable(value = "DEVICE_MAPPER",key="#p0")
    @Override
    public String getDeviceId(String deviceFiled) {
        DeviceMapper device = deviceMapperDao.selectOne(new QueryWrapper<DeviceMapper>().eq("device_name", deviceFiled).or().eq("uuid",deviceFiled));
        return device.getUuid();
    }

    @Override
    public BaseResult reportStampRecord(List<UseStampRecord> useStampRecords){
        try {
            // 获取申请单Id
            String applyId = useStampRecords.get(0).getApplyId();
            // 查看申请单所属哪一个公司
            ProcessDetail processDetail = processDetailDao.selectById(Integer.parseInt(applyId));
            CompInfo compInfo = getCompInfo(processDetail.getCompId().toString());
            TempleteInfo templeteInfo = dingDingService.getTempleteInfo(compInfo.getCorpId(),processDetail.getUserid(), stamperPara.getName());
            if(templeteInfo == null){
                return BaseResultFactory.produceEmptyResult(Code.TEMPLETE_MISSING);
            }
            List<String> opers = processRecordDao.getApproveOper(Integer.parseInt(applyId));
            return dingDingService.createInfo(compInfo,templeteInfo, opers.toArray(new String[]{}), useStampRecords);
        } catch (Exception e) {
            log.info("印章上报过程中遇到错误{}",e);
            return BaseResultFactory.produceEmptyResult(Code.ERROR,e.getMessage());
        }
    }

    @Override
    @Cacheable(value = "COMP_INFO",key="#p0",unless = "#result == null")
    public CompInfo getCompInfo(String corpId) {
        int compId = 0;
        try{
            compId = Integer.parseInt(corpId);
        }catch (Exception e){}
        int finalCompId = compId;
        return compInfoDao.selectOne(new QueryWrapper<CompInfo>()
                .eq("status",true).and(qw -> qw.eq("corp_id",corpId).or().eq("comp_id", finalCompId)));
    }

    @Override
    @CacheEvict(value = "COMP_INFO",key="#p0")
    public BaseResult updateCompStatus(String corpId) {
        int update = compInfoDao.update(null, new UpdateWrapper<CompInfo>().eq("corp_id", corpId).set("status", false));
        if(update == 0){
            return BaseResultFactory.produceError();
        }else{
            return BaseResultFactory.produceSuccess();
        }
    }
}
